﻿#include <QTimer>
#include <QSharedPointer>
#include <QDebug>
#include <QElapsedTimer>
#include "JZCoCoroutine.h"

//JZCoCoroutine
JZCoCoroutine::JZCoCoroutine()
{
    m_ctx = createContext(this);
    m_interrupt = false;
    m_finish = false;
}

JZCoCoroutine::~JZCoCoroutine()
{
    Q_ASSERT(!m_ctx); //在调度器中释放
}

void JZCoCoroutine::setTask(const std::function<void()>& func)
{
    m_func = func;
}

void JZCoCoroutine::run()
{
    if(m_func)
        m_func();
}

void JZCoCoroutine::quit()
{
    m_interrupt = true;
}

bool JZCoCoroutine::isInterrupt() const
{
    return m_interrupt;
}

bool JZCoCoroutine::isFinish() const
{
    return m_finish;
}

void JZCoCoroutine::beforeYield()
{
}

void JZCoCoroutine::beforeResume()
{
}

void JZCoCoroutine::endTask()
{
}

//JZCoThread
JZCoThread::JZCoThread(std::function<void()> func)
{
    m_func = func;
}

void JZCoThread::run()
{
    m_func();
}

//JZCoScheduler
JZCoScheduler* JZCoScheduler::instance()
{
    static JZCoScheduler inst;
    return &inst;
}

JZCoScheduler::JZCoScheduler()
{
    m_mainCtx = nullptr;
    m_co = nullptr;
}

JZCoScheduler::~JZCoScheduler()
{
    onClearContext();
    if(m_mainCtx)
        destoryMainContext(m_mainCtx);
}

void JZCoScheduler::init()
{
    m_mainCtx = createMainContext();
}

bool JZCoScheduler::isInCoroutine()
{
    return (m_co != nullptr);
}

void JZCoScheduler::yield(JZCoCoroutine* co)
{
    Q_ASSERT(co == m_co);

    co->beforeYield();

    CO_CONTEXT_HANDLE cur = getCoContextHandle(co->m_ctx);
    auto pre_co = co->m_preCoList.back();
    co->m_preCoList.pop_back();
    CO_CONTEXT_HANDLE next;
    if (pre_co)
        next = getCoContextHandle(pre_co->m_ctx);
    else
        next = getMainContextHandle(g_scheduler->mainContext());

    m_co = pre_co;
    switchCoroutine(cur, next);
}

void JZCoScheduler::resume(JZCoCoroutine* co)
{
    Q_ASSERT(co && co != m_co);

    co->m_preCoList.push_back(m_co);
    co->beforeResume();

    CO_CONTEXT_HANDLE cur;
    if (m_co)
        cur = getCoContextHandle(m_co->m_ctx);
    else
        cur = getMainContextHandle(g_scheduler->mainContext());

    CO_CONTEXT_HANDLE next = getCoContextHandle(co->m_ctx);
    
    m_co = co;
    switchCoroutine(cur, next);
}

void JZCoScheduler::startTask(JZCoCoroutinePtr task)
{
    task->m_finish = false;
    m_coList.push_back(task);
    resume(task.data());
}

void JZCoScheduler::endTask(JZCoCoroutine *task)
{
    Q_ASSERT(task == m_co);
    Q_ASSERT(task->m_preCoList.size() == 1);

    m_co = nullptr;
    auto ctx = task->m_ctx;
    m_finished << ctx;

    CO_CONTEXT_HANDLE cur = getCoContextHandle(task->m_ctx);

    auto pre_co = task->m_preCoList.back();
    CO_CONTEXT_HANDLE next;
    if (pre_co)
        next = getCoContextHandle(pre_co->m_ctx);
    else
        next = getMainContextHandle(g_scheduler->mainContext());

    task->m_finish = true;
    task->m_ctx = nullptr;
    for (int i = 0; i < m_coList.size(); i++)
    {
        if (m_coList[i].data() == task)
        {
            task->endTask();
            m_coList.removeAt(i);
            break;
        }
    }

    QTimer::singleShot(0, this, &JZCoScheduler::onClearContext);
    switchCoroutine(cur,next);
}

JZCoMainContext* JZCoScheduler::mainContext()
{
    return m_mainCtx;
}

JZCoCoroutine* JZCoScheduler::currentCoroutine()
{
    return m_co;
}

void JZCoScheduler::onClearContext()
{
    for (int i = 0; i < m_finished.size(); i++)
        destoryContext(m_finished[i]);
    m_finished.clear();
}